#include "driver.h"

//----------------------------------------------------------------------
//
// DDQueryFileComplete
//
// This routine is used to handle I/O completion for our self-generated
// IRP that is used to query a file's name or number.
//
//----------------------------------------------------------------------
NTSTATUS 
DDQueryFileComplete(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp,
    PVOID Context
    )
{
    //
    // Copy the status information back into the "user" IOSB.
    //
    *Irp->UserIosb = Irp->IoStatus;
    if( !NT_SUCCESS(Irp->IoStatus.Status) ) {

        DbgMess(("   ERROR ON IRP: %x\n", Irp->IoStatus.Status ));
    }
    
    //
    // Set the user event - wakes up the mainline code doing this.
    //
    KeSetEvent(Irp->UserEvent, 0, FALSE);
    
    //
    // Free the IRP now that we are done with it.
    //
    IoFreeIrp(Irp);
    
    //
    // We return STATUS_MORE_PROCESSING_REQUIRED because this "magic" return value
    // tells the I/O Manager that additional processing will be done by this driver
    // to the IRP - in fact, it might (as it is in this case) already BE done - and
    // the IRP cannot be completed.
    //
    return STATUS_MORE_PROCESSING_REQUIRED;
}


//----------------------------------------------------------------------
//
// DDQueryFile
//
// This function retrieves the "standard" information for the
// underlying file system, asking for the filename in particular.
//
//----------------------------------------------------------------------
BOOLEAN 
DDQueryFile( 
    PDEVICE_OBJECT DeviceObject, 
    PFILE_OBJECT FileObject,
    FILE_INFORMATION_CLASS FileInformationClass,
    PVOID FileQueryBuffer,
    ULONG FileQueryBufferLength
    )
{
    PIRP irp;
    KEVENT event;
    IO_STATUS_BLOCK IoStatusBlock;
    PIO_STACK_LOCATION ioStackLocation;

	//DeviceObject = IoGetRelatedDeviceObject(FileObject);

    DbgMess(("Getting file name for %x\n", FileObject));

    //
    // Initialize the event
    //
    KeInitializeEvent(&event, SynchronizationEvent, FALSE);

    //
    // Allocate an irp for this request.  This could also come from a 
    // private pool, for instance.
    //
    //irp = IoAllocateIrp(IoGetRelatedDeviceObject(FileObject)->StackSize, FALSE);
    irp = IoAllocateIrp(DeviceObject->StackSize, FALSE);
    if(!irp) {

        //
        // Failure!
        //
        return FALSE;
    }
  
    //
    // Build the IRP's main body
    //  
    irp->AssociatedIrp.SystemBuffer = FileQueryBuffer;
    irp->UserEvent = &event;
    irp->UserIosb = &IoStatusBlock;
    irp->Tail.Overlay.Thread = PsGetCurrentThread();
    irp->Tail.Overlay.OriginalFileObject = FileObject;
    irp->RequestorMode = KernelMode;
    irp->Flags = 0;

    //
    // Set up the I/O stack location.
    //
    ioStackLocation = IoGetNextIrpStackLocation(irp);
    ioStackLocation->MajorFunction = IRP_MJ_QUERY_INFORMATION;
    ioStackLocation->DeviceObject = DeviceObject;
    ioStackLocation->FileObject = FileObject;
    //ioStackLocation->Parameters.QueryFile.Length = FileQueryBufferLength;
    ioStackLocation->Parameters.QueryFile.Length = sizeof(FILE_STANDARD_INFORMATION);
    //ioStackLocation->Parameters.QueryFile.FileInformationClass = FileInformationClass;
	ioStackLocation->Parameters.QueryFile.FileInformationClass = FileStandardInformation;

    //
    // Set the completion routine.
    //
    IoSetCompletionRoutine(irp, DDQueryFileComplete, 0, TRUE, TRUE, TRUE);

    //
    // Send it to the FSD
    //
	DbgMess(("Realted is %X, my is %X", IoGetRelatedDeviceObject(FileObject), DeviceObject));
    //(void) IoCallDriver(IoGetRelatedDeviceObject(FileObject), irp);
    (void) IoCallDriver(DeviceObject, irp);

    //
    // Wait for the I/O
    //
    KeWaitForSingleObject(&event, Executive, KernelMode, TRUE, 0);

	DbgMess(("DDQueryFile: status is %X", IoStatusBlock.Status));
	
	*((PFILE_NAME_INFORMATION)FileQueryBuffer)->FileName = 0;
	DbgMess(("file is %ws", ((PFILE_NAME_INFORMATION)FileQueryBuffer)->FileName));

    //
    // Done! Note that since our completion routine frees the IRP we cannot 
    // touch the IRP now.
    //
    return NT_SUCCESS( IoStatusBlock.Status );
}


//----------------------------------------------------------------------
//
// DDGetFullPath
//
// Takes a fileobject and filename and returns a canonical path
//
//----------------------------------------------------------------------
VOID 
DDGetFullPath( 
    BOOLEAN createPath, 
    PFILE_OBJECT fileObject, 
    PDD_DEVICE hookExt, 
    PCHAR fullPathName 
    )
{
    ULONG               pathLen, prefixLen;//, slashes;
    PCHAR               pathOffset;//, ptr;
    BOOLEAN             gotPath;
    PFILE_OBJECT        relatedFileObject;
    //PHASH_ENTRY         hashEntry, newEntry;
    ANSI_STRING         fileName;
    ANSI_STRING         relatedName;
    PFILE_NAME_INFORMATION fileNameInfo;
    //LARGE_INTEGER fileInternalInfo;
    UNICODE_STRING      fullUniName;
    //ULONGLONG           mftIndex;

    //
    // Calculate prefix length
    //
	if( !fileObject ||
		fileObject->DeviceObject->DeviceType == FILE_DEVICE_NETWORK_FILE_SYSTEM ) {

			prefixLen = 0;

	} else {

		prefixLen = 0; // "C:"
	}

    //
    // If there's no file object, we can't even ask for a name.
    //
	if( !fileObject ) {
		sprintf( fullPathName, "FileObject is null");
        return;
    }

    //
    // Initialize variables
    //
    fileName.Buffer = NULL;
    relatedName.Buffer = NULL;
    gotPath = FALSE;

    //
    // If we are not in the create path, we can ask the file system for the name. If we
    // are in the create path, we can't ask the file system for the name of the file object, since
    // the file system driver hasn't even seen the file object yet.
    //
    if( !gotPath && !createPath ) {

        //
        // Ask the file system for the name of the file, which its required to be
        // able to provide for the Win32 filename query function. We could use the
        // undocumented ObQueryNameString, but then we'd have to worry about
        // re-entrancy issues, since that call generates the IRP that we create
        // manually here. Since we send the IRP to the FSD below us, we don't need
        // to worry about seeing the IRP in our dispatch entry point. This can fail
        // in some cases, so we fall back on constructing the name ourselves if
        // we have to.
        //
		
        fileNameInfo = (PFILE_NAME_INFORMATION) ExAllocatePool( NonPagedPool, 
                                                                MAXPATHLEN*sizeof(WCHAR) );

        if( fileNameInfo && 
            DDQueryFile(hookExt->FileSystem, fileObject, FileNameInformation, 
                             fileNameInfo, (MAXPATHLEN - prefixLen - 1)*sizeof(WCHAR) )) {

            fullUniName.Length = (SHORT) fileNameInfo->FileNameLength;
            fullUniName.Buffer = fileNameInfo->FileName;
            if( NT_SUCCESS( RtlUnicodeStringToAnsiString( &fileName, &fullUniName, TRUE ))) { 

                fullPathName[ fileName.Length + prefixLen ] = 0;

                RtlCopyMemory( &fullPathName[prefixLen], fileName.Buffer, fileName.Length );
                gotPath = TRUE;
                RtlFreeAnsiString( &fileName );
                fileName.Buffer = NULL;
            }
        } 
        if( fileNameInfo ) ExFreePool( fileNameInfo );
    }

    //
    // If we don't have a name yet then we are in the create path, or we failed
    // when we asked the file system for the name. In that case we'll go ahead
    // and construct the name based on file object names.
    //
    if( !gotPath ) {

        //
        // If there is no file name at this point, just return "DEVICE" to indicate
        // raw access to a device
        //
        if( !fileObject->FileName.Buffer ) {

            sprintf( fullPathName, "DEVICE");
            return;
        }
    
        //
        // Create the full path name. First, calculate the length taking into 
        // account space for seperators and the leading prefix
        //
        if( !NT_SUCCESS( RtlUnicodeStringToAnsiString( &fileName, &fileObject->FileName, TRUE ))) {

            sprintf( fullPathName, "<Out of Memory>");
            return;
        }

        pathLen = fileName.Length + prefixLen;
        relatedFileObject = fileObject->RelatedFileObject;
    
        //
        // Only look at related file object if this is a relative name
        //
        if( fileObject->FileName.Buffer[0] != L'\\' && 
            relatedFileObject && relatedFileObject->FileName.Length ) {
	        
			if( !NT_SUCCESS( RtlUnicodeStringToAnsiString( &relatedName, &relatedFileObject->FileName, TRUE ))) {

                sprintf( fullPathName, "<Out of Memory>" );
                RtlFreeAnsiString( &fileName );
                return;
            }
            pathLen += relatedName.Length+1;
        }

        //
        // If the name is too long, quit now
        //
        if( pathLen >= MAXPATHLEN ) {
            
            strcat( fullPathName, " <Name Too Long>" );

        } else {
    
            //
            // Now we can build the path name
            //
            fullPathName[ pathLen ] = 0;
            
            pathOffset = fullPathName + pathLen - fileName.Length;
            memcpy( pathOffset, fileName.Buffer, fileName.Length + 1 );
    
            if( fileObject->FileName.Buffer[0] != L'\\' && 
                relatedFileObject && relatedFileObject->FileName.Length ) {

                //
                // Copy the component, adding a slash separator
                //
                *(pathOffset - 1) = '\\';
                pathOffset -= relatedName.Length + 1;
                    
                memcpy( pathOffset, relatedName.Buffer, relatedName.Length );

                //
                // If we've got to slashes at the front zap one
                //
                /*if( pathLen > 3 && fullPathName[2] == '\\' && fullPathName[3] == '\\' )  {
                    
                    strcpy( fullPathName + 2, fullPathName + 3 );
                }*/
            }
        }  
    } 
    if( fileName.Buffer ) RtlFreeAnsiString( &fileName );
    if( relatedName.Buffer ) RtlFreeAnsiString( &relatedName );

}
